#pragma once
#include <iostream>
#include <thread>
#include <mutex>
#include <shared_mutex>
#define MESSAGE "HTCK_DEVICE"
class Discovery {
    std::shared_mutex _readWriteLock;
    std::atomic<bool> _stop=false;
    std::map<std::string,int> _ips;
public:
    Discovery() {
        std::thread([&]{
            boost::asio::io_service io;
            boost::system::error_code ec;
            boost::asio::ip::udp::socket socket(io,boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 9001));
            boost::property_tree::ptree settings;
            boost::property_tree::ini_parser::read_ini("config.ini", settings);
            socket.set_option(boost::asio::ip::multicast::hops(10),ec);
            socket.set_option(boost::asio::socket_base::keep_alive(true),ec);
            boost::asio::ip::udp::endpoint multicast_endpoint(boost::asio::ip::address::from_string("224.0.0.1"), 9001);
            socket.set_option(boost::asio::ip::multicast::join_group(multicast_endpoint.address()),ec);
            if (ec) {
                std::cout<< "Failed to join multicast group"<<std::endl;
                return;
            }
            while (!_stop) {
                std::string sendgram(MESSAGE);
                sendgram+=":";
                sendgram+=settings.get<std::string>("http.port", "80");
                sendgram+="_";
                sendgram+=settings.get<std::string>("device/type", "1");
                socket.send_to(boost::asio::buffer(sendgram),multicast_endpoint);
                std::this_thread::sleep_for(std::chrono::seconds(3));

                long long recv_len;
                do{
                    std::vector<char> datagram(1024,0);
                    boost::asio::ip::udp::endpoint sender_endpoint;
                    recv_len = socket.receive_from(boost::asio::buffer(datagram), sender_endpoint);
                    if(recv_len && std::string(&datagram[0])._Starts_with(MESSAGE)){
                        std::string ip = sender_endpoint.address().to_string()+std::string(&datagram[0]).substr(strlen(MESSAGE));
                        std::unique_lock<std::shared_mutex> lock(_readWriteLock);
                        if(_ips.find(ip)==_ips.end())
                            std::cout<<"add device:"<<ip<<std::endl;
                        _ips[ip]=10;
                    }
                }while(recv_len>0);
                {
                    std::unique_lock<std::shared_mutex> lock(_readWriteLock);
                    for(auto it=_ips.begin();it!=_ips.end();){
                        (*it).second--;
                        if ((*it).second==0){
                            std::cout<<"delete device:"<<(*it).first<<std::endl;
                            it = _ips.erase(it);
                        }
                        else
                            it++;
                    }
                }
            }
            socket.set_option(boost::asio::ip::multicast::leave_group(multicast_endpoint.address()),ec);
            socket.close();
        }).detach();
    }
    ~Discovery() {_stop=true;}
    std::vector<std::string> getips(){
        std::vector<std::string> ips;
        {
            std::unique_lock<std::shared_mutex> lock(_readWriteLock);
            std::for_each(_ips.begin(),_ips.end(),[&](std::pair<const std::string,int>& it){ ips.emplace_back(it.first);});
        }
        return ips;
    }
};